package net.wicp.tams.common.redis.pool;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.GenericObjectPool;

import com.alibaba.fastjson.JSON;

import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.ReflectAssist;
import net.wicp.tams.common.apiext.StringUtil;
import redis.clients.jedis.Jedis;
import redis.clients.util.Pool;

public abstract class AbsPool {
	/*****
	 * 把对象做为Map存放到Redis
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param obj
	 *            要放入的对象
	 * @param <T>
	 *            对象的类型
	 * @param expire
	 *            超时时间，单位（秒）
	 */
	public static <T extends Serializable> void putObjByMap(Jedis jedis, String key, T obj, Integer expire) {
		Map<String, String> inpumap = ReflectAssist.convertMapFromBean(obj);
		jedis.hmset(key, inpumap);
		if (expire != null) {
			jedis.expire(key, expire);
		}
	}

	/***
	 * 把对象做为Map存放到Redis，没有超时时间，永久性放入
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param <T>
	 *            对象的类型
	 * @param obj
	 *            要放入的对象
	 */
	public static <T extends Serializable> void putObjByMap(Jedis jedis, String key, T obj) {
		putObjByMap(jedis, key, obj, null);
	}

	/***
	 * 把对象做为Json存放到Redis
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param obj
	 *            要放入的对象
	 * @param key
	 *            要放入的key值
	 * @param <T>
	 *            对象的类型
	 * @param expire
	 *            超时时间，单位（秒）
	 */
	public final static <T extends Serializable> void putObjByJson(Jedis jedis, T obj, String key, Integer expire) {
		String json = JSON.toJSONString(obj);
		jedis.set(key, json);
		if (expire != null) {
			jedis.expire(key, expire);
		}
	}

	/****
	 * 把对象做为Json存放到Redis
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param obj
	 *            要放入的对象
	 * @param <T>
	 *            对象的类型
	 * @param key
	 *            要放入的key值
	 */
	public final static <T extends Serializable> void putObjByJson(Jedis jedis, T obj, String key) {
		putObjByJson(jedis, obj, key, null);
	}

	/***
	 * 枚举类型的map放到缓存
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param <T>
	 *            对象的类型
	 * @param inputMap
	 *            要存放的map，它的key是一个枚举值
	 */

	public final static <T extends Enum> void putEnumMap(Jedis jedis, String key, Map<T, String> inputMap) {
		if (jedis == null || MapUtils.isEmpty(inputMap) || StringUtil.isNull(key)) {
			return;
		}
		Map<String, String> input = new HashMap<>();
		for (Enum ele : inputMap.keySet()) {
			input.put(ele.name(), inputMap.get(ele));
		}
		jedis.hmset(key, input);
	}

	/****
	 * 得到枚举类的缓存对象
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param clazz
	 *            枚举所对应的类
	 * @return 返回的枚举
	 */

	public final static <T extends Enum> Map<T, String> getEnumMap(Jedis jedis, String key, Class clazz) {
		Object[] objs = clazz.getEnumConstants();
		String[] fields = new String[objs.length];
		for (int i = 0; i < fields.length; i++) {
			Enum tempobj = (Enum) objs[i];
			fields[i] = tempobj.name();
		}
		List<String> rets = jedis.hmget(key, fields);
		Map<T, String> retobj = new HashMap<>();
		for (int i = 0; i < fields.length; i++) {
			Enum curobj = null;
			for (Object object : objs) {
				Enum tempobj = (Enum) object;
				if (fields[i].equals(tempobj.name())) {
					curobj = tempobj;
					break;
				}
			}
			retobj.put((T) curobj, rets.get(i));
		}
		return retobj;
	}

	/***
	 * 取指定列的值
	 * 
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param fields
	 *            要取的列名
	 * @return
	 */
	public static Map<String, String> getMapByField(Jedis jedis, String key, String... fields) {
		Map<String, String> retobj = new HashMap<String, String>();
		if (jedis == null || StringUtil.isNull(key) || ArrayUtils.isEmpty(fields) || !jedis.exists(key)) {
			return retobj;
		}
		List<String> values = jedis.hmget(key, fields);
		if (ArrayUtils.isNotEmpty(fields) && CollectionUtils.isNotEmpty(values)) {
			for (int i = 0; i < fields.length; i++) {
				retobj.put(fields[i], values.get(i));
			}
		}
		return retobj;
	}

	/***
	 * Redis上的值为Map,取对象的值，没有指定字段就取全部
	 * 
	 * @param clazz
	 *            要返回的对象的类
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param <T>
	 *            对象的类型
	 * @param fields
	 *            要取的列名
	 * @return 要返回的对象
	 */
	public static <T extends Serializable> T getObjByMapValue(Class<T> clazz, Jedis jedis, String key,
			String... fields) {
		if (ArrayUtils.isEmpty(fields)) {
			List<String> classfields = ReflectAssist.findGetField(clazz);
			fields = classfields.toArray(new String[classfields.size()]);
		}
		Map<String, String> retmap = getMapByField(jedis, key, fields);
		T rett = (T) ReflectAssist.convertMapToBean(clazz, retmap);
		return rett;
	}

	public static <T extends Serializable> T getObjByMapValue(Class<T> clazz, Jedis jedis, String key) {
		List<String> list = ReflectAssist.findGetField(clazz);
		String[] array = list.toArray(new String[list.size()]);
		return getObjByMapValue(clazz, jedis, key, array);
	}

	/***
	 * Redis上的值为Json,取对象的值
	 * 
	 * @param clazz
	 *            要返回的对象的类
	 * @param jedis
	 *            连接的引用，外部传入
	 * @param key
	 *            要放入的key值
	 * @param <T>
	 *            对象的类型
	 * @return
	 */
	public static <T extends Serializable> T getObjByJsonValue(Class<T> clazz, Jedis jedis, String key) {
		String jsonstr = jedis.get(String.valueOf(key));
		T retobj = (T) JSON.toJavaObject(JSON.parseObject(jsonstr), clazz);
		return retobj;
	}

	/***
	 * 释放资源
	 *
	 * @param jedis
	 *            要释放的连接
	 */
	public static void returnResource(Jedis jedis) {
		if (jedis != null) {
			try {
				jedis.close();
			} catch (Exception e) {

			}
		}
	}

	public abstract Jedis getResource();

	/***
	 * 池废弃
	 */
	public abstract void destroy();

	public abstract boolean isInit();
	
	
	protected void doLeak(Pool<Jedis> jedisPool) {
		if (Conf.getInt("common.redis.redisserver.leak.second")>0) {
			AbandonedConfig abandonedConfig = new AbandonedConfig();
			abandonedConfig.setRemoveAbandonedOnMaintenance(true); // 在Maintenance的时候检查是否有泄漏
			abandonedConfig.setRemoveAbandonedOnBorrow(true); // borrow 的时候检查泄漏
			abandonedConfig.setRemoveAbandonedTimeout(Conf.getInt("common.redis.redisserver.leak.second")); // 如果一个对象borrow之后默认10秒还没有返还给pool，认为是泄漏的对象
			@SuppressWarnings("unchecked")
			GenericObjectPool<Jedis> poolInner = (GenericObjectPool<Jedis>) ReflectAssist.getPrivateField(jedisPool,
					"internalPool");
			poolInner.setAbandonedConfig(abandonedConfig);
		}
	}

	///////////////////////////////////// 一些工具//////////////////////////////////////////////////////////////////
	public <T extends Serializable> T findByRedis(Class<T> calssz, String key) {
		Jedis jedis = getResource();
		try {
			T t = AbsPool.getObjByMapValue(calssz, jedis, key);
			return t;
		} finally {
			AbsPool.returnResource(jedis);
		}
	}

	public <T extends Serializable> void putRedis(T saveobj, String key) {
		Jedis jedis = getResource();
		try {
			AbsPool.putObjByMap(jedis, key, saveobj);
		} finally {
			AbsPool.returnResource(jedis);
		}
	}
}
